import inspect
import logging
from datetime import timedelta
from functools import wraps
from typing import Any, Callable, Union

from afast_core.core_libs.redis_cache import RedisCacheFactory
from afast_core.core_libs.redis_cache.config import DEFAULT_EXPIRE_TIME

log = logging.getLogger(__name__)
log.setLevel(logging.INFO)


def cache(
        expire: Union[timedelta, int] = DEFAULT_EXPIRE_TIME,
        namespace: str = None,
        key_prefix: str = None,
        cust_key: str = None,
        key_builder: Callable[..., Any] = None,
        encoder: Callable[..., Any] = None,
        decoder: Callable[..., Any] = None
) -> Callable:
    """缓存装饰器，用于同步和异步函数装饰处理"""

    def cache_wrap(f: Callable[..., Any]):

        @wraps(f)
        async def async_wrapper(*args, **kwargs):
            nonlocal namespace
            nonlocal encoder
            nonlocal decoder
            nonlocal cust_key
            # 获取一个缓存工厂的实例对象
            cache_instance = RedisCacheFactory.get_async_instance()
            # 如果当前的缓存工厂对象已创建
            if cache_instance is not None:
                # 生成我们的缓存的KEY
                key_generator = key_builder or cache_instance.key_builder
                # 自定义命名空间
                namespace = namespace or cache_instance.namespace
                # 自定义前缀
                prefix = key_prefix or cache_instance.key_prefix
                # 自定义的加密和解密的方式
                cust_key = cust_key or None
                encoder = encoder or cache_instance.encoder
                decoder = decoder or cache_instance.decoder
            if cache_instance is None:
                return await f(*args, **kwargs)
            # 缓存的KEY生成
            key = cust_key or key_generator(f.__module__, f.__name__, args, kwargs, namespace=namespace, prefix=prefix)
            # 判断当前的KEY是否存在数据了
            cached_data = await cache_instance.get_key(key)
            # 如果存在了数据
            if cached_data:
                log.info("data exist in cache")
                log.info("return data from cache")
                # print("从缓存中返回！")
                # 直接的解码缓存数据并返回
                return decoder(cached_data)
            # 如果没有缓存，那么继续往下的执行
            result = await f(*args, **kwargs)
            # 返回路由函数结果后直接的设置缓存结果
            await cache_instance.set_key(key, encoder(result), expire)
            log.info("set result in cache")
            return result

        @wraps(f)
        def wrapper(*args, **kwargs):
            nonlocal namespace
            nonlocal encoder
            nonlocal decoder
            nonlocal cust_key

            cache_instance = RedisCacheFactory.get_sync_instance()
            if cache_instance is not None:
                key_generator = key_builder or cache_instance.key_builder
                namespace = namespace or cache_instance.namespace
                prefix = key_prefix or cache_instance.key_prefix
                # 自定义的加密和解密的方式
                cust_key = cust_key or None
                encoder = encoder or cache_instance.encoder
                decoder = decoder or cache_instance.decoder
            if cache_instance is None:
                return f(*args, **kwargs)
            key = cust_key or key_generator(
                f.__module__,
                f.__name__,
                args,
                kwargs,
                namespace=namespace,
                prefix=prefix,
            )
            cached_data = cache_instance.get_key(key)
            if cached_data:
                log.info("data exist in cache")
                log.info("return data from cache")
                return decoder(cached_data)
            result = f(*args, **kwargs)
            cache_instance.set_key(key, encoder(result), expire)
            log.info("set result in cache")
            return result

        return async_wrapper if inspect.iscoroutinefunction(f) else wrapper

    return cache_wrap


def updata_cache(
        expire: Union[timedelta, int] = DEFAULT_EXPIRE_TIME,
        namespace: str = None,
        key_prefix: str = None,
        cust_key: str = None,
        key_builder: Callable[..., Any] = None,
        encoder: Callable[..., Any] = None,
        decoder: Callable[..., Any] = None
) -> Callable:
    """缓存装饰器，用于同步和异步函数装饰处理"""

    def cache_wrap(f: Callable[..., Any]):

        @wraps(f)
        async def async_wrapper(*args, **kwargs):
            nonlocal namespace
            nonlocal encoder
            nonlocal decoder
            nonlocal cust_key

            # 获取一个缓存工厂的实例对象
            cache_instance = RedisCacheFactory.get_async_instance()
            # 如果当前的缓存工厂对象已创建
            if cache_instance is not None:
                # 生成我们的缓存的KEY
                key_generator = key_builder or cache_instance.key_builder
                # 自定义命名空间
                namespace = namespace or cache_instance.namespace
                # 自定义前缀
                prefix = key_prefix or cache_instance.key_prefix
                # 自定义的加密和解密的方式
                cust_key = cust_key or None
                # 自定义的加密和解密的方式
                encoder = encoder or cache_instance.encoder
                decoder = decoder or cache_instance.decoder
            if cache_instance is None:
                return await f(*args, **kwargs)
            # 缓存的KEY生成
            key = cust_key or key_generator(f.__module__, f.__name__, args, kwargs, namespace=namespace, prefix=prefix)
            # 判断当前的KEY是否存在数据了
            cached_data = await cache_instance.get_key(key)
            await cache_instance.clear_keys(key)
            # 如果存在了数据
            if cached_data:
                await cache_instance.clear_keys(key)

            # 如果没有缓存，那么继续往下的执行
            result = await f(*args, **kwargs)
            # 返回路由函数结果后直接的设置缓存结果
            await cache_instance.set_key(key, encoder(result), expire)

            return result

        @wraps(f)
        def wrapper(*args, **kwargs):
            nonlocal namespace
            nonlocal encoder
            nonlocal decoder
            nonlocal cust_key

            cache_instance = RedisCacheFactory.get_sync_instance()
            if cache_instance is not None:
                key_generator = key_builder or cache_instance.key_builder
                namespace = namespace or cache_instance.namespace
                prefix = key_prefix or cache_instance.key_prefix
                # 自定义的加密和解密的方式
                cust_key = cust_key or None
                encoder = encoder or cache_instance.encoder
                decoder = decoder or cache_instance.decoder
            if cache_instance is None:
                return f(*args, **kwargs)
            key = cust_key or key_generator(
                f.__module__,
                f.__name__,
                args,
                kwargs,
                namespace=namespace,
                prefix=prefix,
            )
            cached_data = cache_instance.get_key(key)
            if cached_data:
                cache_instance.clear_keys(key)
            result = f(*args, **kwargs)
            cache_instance.set_key(key, encoder(result), expire)
            log.info("set result in cache")
            return result

        return async_wrapper if inspect.iscoroutinefunction(f) else wrapper

    return cache_wrap
